import time
import pytz
import pandas as pd

import coin2.service.strategy.trading_summary_service_pb2 as ts_pb2
from absl import app, flags
from tabulate import tabulate
from datetime import datetime, timedelta, time
from xunkemgmt_client.tool.slack_noti import send_to_slack
from xunkemgmt_client.client.api_client import XunkemgmtClient
from coin.base.datetime_util import to_timestamp_int
from xunkemgmt_client.support.pta.util.proto_util import (
    convert_pta_proto_to_df,
    convert_qts_rsp_to_strategy_summary)

FLAGS = flags.FLAGS
KEY_COLUMNS = ['strategy_name', 'accounting_currency', 'trader']


def _get_interval_trading_summary(trading_date,
                                  business_units,
                                  strategy_ids=None,
                                  strategy_names=None,
                                  strategy_groups=None,
                                  traders=None):
  request = ts_pb2.QueryTradingSummaryIntervalHistoriesRequestProto(
      start_timestamp=to_timestamp_int(trading_date),
      end_timestamp=to_timestamp_int(trading_date + timedelta(days=1)),
      business_units=business_units,
      strategy_ids=strategy_ids,
      strategy_names=strategy_names,
      strategy_groups=strategy_groups,
      traders=traders
  )
  with XunkemgmtClient() as client:
    response = client.query_trading_summary_interval_histories(request)
  trading_summaries = response.histories
  return trading_summaries


def _get_snapshot_trading_summary(query_time,
                                  business_units,
                                  strategy_ids=None,
                                  strategy_names=None,
                                  strategy_groups=None,
                                  traders=None):
  request = ts_pb2.QueryTradingSummarySnapshotsRequestProto(
      query_ts=to_timestamp_int(datetime.combine(query_time, time.max)),
      business_units=business_units,
      strategy_ids=strategy_ids,
      strategy_names=strategy_names,
      strategy_groups=strategy_groups,
      traders=traders
  )
  with XunkemgmtClient() as client:
    response = client.query_trading_summary_snapshots(request)
  trading_summaries = response.snapshots
  return trading_summaries


def _convert_df(response_list):
  summary = convert_qts_rsp_to_strategy_summary(response_list)
  summary_df = convert_pta_proto_to_df(summary)
  if not summary_df.empty:
    summary_df = summary_df[pd.isnull(summary_df['symbol'])]
    summary_df = summary_df[[
      'strategy_name', 'strategy_group', 'trader',
      'accounting_currency', 'business_unit', 'machine',
      'pnl_mark_in_usd', 'pnl_mark_from_fill_in_usd'
      ]]
    summary_df = summary_df.groupby(
      KEY_COLUMNS, as_index=False).sum(numeric_only=True, min_count=1)
  return summary_df


def _check_BNB_purchase(compare_df, diff_amount_thold):
  new_compare_df = compare_df.copy()
  if not new_compare_df.empty:
    tmp_df = compare_df.groupby(['strategy_name', 'trader'], as_index=False).sum()
    tmp_df['pnl_diff'] = tmp_df['pnl_mark_in_usd'] - tmp_df['pnl_mark_from_fill_in_usd']
    if diff_amount_thold:
      drop_list = tmp_df[tmp_df['pnl_diff'] < diff_amount_thold]['strategy_name'].to_list()
      new_compare_df = compare_df[~compare_df['strategy_name'].isin(drop_list)]
  return new_compare_df


def compare_consistency(interval_df, snapshot_df):
  compare_df = pd.DataFrame()
  new_interval_df = interval_df.copy()
  new_snapshot_df = snapshot_df.copy()
  if len(new_interval_df) != len(new_snapshot_df):
    new_interval_df['source'] = 'interval'
    new_snapshot_df['source'] = 'snapshot'
    compare_df = pd.concat([new_interval_df, new_snapshot_df])
    compare_df = compare_df.drop_duplicates(subset=KEY_COLUMNS, keep=False)
  return compare_df


def compare_inter_pnl(interval_df, snapshot_df, diff_amount_thold=None, diff_pct_thold=None):
  compare_df = interval_df.merge(snapshot_df, how='inner', on=KEY_COLUMNS, suffixes=('', '_snapshot'))
  compare_df.drop(['pnl_mark_from_fill_in_usd', 'pnl_mark_from_fill_in_usd_snapshot'], axis=1, inplace=True)
  compare_df['pnl_diff'] = abs(compare_df['pnl_mark_in_usd'] - compare_df['pnl_mark_in_usd_snapshot'])
  compare_df['pnl_diff_pct(%)'] = abs(compare_df['pnl_diff'] / compare_df['pnl_mark_in_usd']) * 100
  if diff_amount_thold:
    compare_df = compare_df[compare_df['pnl_diff'] >= diff_amount_thold]
  if diff_pct_thold:
    compare_df = compare_df[compare_df['pnl_diff_pct(%)'] >= diff_pct_thold]
  compare_df = compare_df.sort_values(['pnl_diff', 'pnl_diff_pct(%)'], ascending=False)
  return compare_df


def compare_intra_pnl(interval_df, diff_amount_thold=None, diff_pct_thold=None):
  compare_df = interval_df.copy()
  compare_df = compare_df[(compare_df['pnl_mark_from_fill_in_usd'] != 0) |
                          (compare_df['accounting_currency'] == 'BNB')]
  compare_df['pnl_diff'] = abs(compare_df['pnl_mark_in_usd'] - compare_df['pnl_mark_from_fill_in_usd'])
  compare_df['pnl_diff_pct(%)'] = abs(compare_df['pnl_diff'] / compare_df['pnl_mark_in_usd']) * 100
  if diff_amount_thold:
    compare_df = compare_df[compare_df['pnl_diff'] >= diff_amount_thold]
  if diff_pct_thold:
    compare_df = compare_df[compare_df['pnl_diff_pct(%)'] >= diff_pct_thold]
  compare_df = _check_BNB_purchase(compare_df, diff_amount_thold)
  compare_df = compare_df.sort_values(['pnl_diff', 'pnl_diff_pct(%)'], ascending=False)
  return compare_df


def _format_df(df):
  if df.empty:
    return 'None'
  return tabulate(df,headers='keys', showindex=False,
                  tablefmt='github', numalign='right')


def main(_):
  if FLAGS.trading_date is not None:
    trading_date = datetime.strptime(FLAGS.trading_date, '%Y%m%d')
  else:
    trading_date = datetime.combine(datetime.utcnow() - timedelta(1), time.min)

  business_units = FLAGS.business_units.strip().split(',')

  intv_strat_summary = _get_interval_trading_summary(trading_date, business_units)
  intv_strat_summary_df = _convert_df(intv_strat_summary)
  if FLAGS.csv_file_path:
    snap_strat_summary_df = pd.read_csv(FLAGS.csv_file_path)
  else:
    snap_strat_summary = _get_snapshot_trading_summary(trading_date, business_units)
    snap_strat_summary_df = _convert_df(snap_strat_summary)

  assert not intv_strat_summary_df.empty, 'empty daily trading summary'
  assert not snap_strat_summary_df.empty, 'empty snapshot trading summary'

  str_rpt = 'trading_date: ' + trading_date.strftime('%Y-%m-%d') + '\n'
  str_rpt += f'# daily trading summary: {len(intv_strat_summary_df)}\n'
  str_rpt += f'# snapshot trading summary: {len(snap_strat_summary_df)}'
  str_rpt += '\n\n'

  consistency_check = compare_consistency(intv_strat_summary_df, snap_strat_summary_df)
  str_rpt += 'trading summraies outer:\n'
  str_rpt += _format_df(consistency_check)
  str_rpt += '\n\n'

  inter_pnl_diff = compare_inter_pnl(
    intv_strat_summary_df, snap_strat_summary_df, diff_amount_thold=1000, diff_pct_thold=FLAGS.diff_pct_thold)
  str_rpt += 'comparaison of daily and snpashot pnl in USD:\n'
  str_rpt += _format_df(inter_pnl_diff)
  str_rpt += '\n\n'

  intra_pnl_diff = compare_intra_pnl(
    intv_strat_summary_df, diff_amount_thold=500, diff_pct_thold=FLAGS.diff_pct_thold)
  str_rpt += 'comparaison of daily pnl and pnl from fill in USD:\n'
  str_rpt += _format_df(intra_pnl_diff)
  
  if len(str_rpt) > 0:
    print(str_rpt)
    if FLAGS.slack_receiver:
      send_to_slack(str_rpt, FLAGS.slack_receiver, 'file')


if __name__ == '__main__':
  flags.DEFINE_string('trading_date', None, '%Y%m%d')
  flags.DEFINE_string('business_units', 'Coin', 'business units')
  flags.DEFINE_string('csv_file_path', None, 'snapshot csv file path')
  flags.DEFINE_string('slack_receiver', None, 'slack channel receiver')
  flags.DEFINE_integer('diff_pct_thold', None, 'pnl diff threshold in pct')
  app.run(main)
